UPDATE v549: Izin Bypass Pengecekan Awal per Unit
             (BD bisa ditutup meski Pengecekan Awal belum lengkap,
              tapi hanya untuk unit yang didaftarkan)

>>> BACKEND BERUBAH — WAJIB RESTART <<<
>>> FRONTEND WAJIB BUILD ULANG <<<
>>> SATU TABEL BARU DIBUAT OTOMATIS SAAT BACKEND NYALA <<<

BERKAS BERUBAH (10)
  backend/routes/masterdata.js                    tabel + 3 endpoint
  backend/routes/hakedit.js                       kunci hak baru
  backend/routes/maintenancerecord.js             jejak di log server
  frontend/src/services/api.js                    ATURANNYA ADA DI SINI
  frontend/src/views/IzinBypassPengecekan.vue     BERKAS BARU
  frontend/src/router/index.js                    route layar baru
  frontend/src/App.vue                            menu Master Data
  frontend/src/views/MonitoringUnit.vue           Close BD + Edit Pelaporan
  frontend/src/views/TiketPerbaikan.vue           Close Tiket
  frontend/src/views/TodoList.vue                 popup part di Outstanding

  TABEL_IZIN_BYPASS_PENGECEKAN.sql                TIDAK WAJIB (lihat bag. 9)


================================================================
BACA INI DULU — SATU AKIBAT YANG LANGSUNG TERASA
================================================================
DAFTAR UNITNYA BERAWAL KOSONG.

Sampai ada unit yang didaftarkan, TIDAK ADA SATU PUN BD yang bisa
ditutup tanpa Pengecekan Awal lengkap — termasuk oleh akun berakses
penuh (31196), yang sebelum update ini bisa menutup apa saja.

Itu memang arti "izin per unit", bukan kelalaian. Tapi kalau Anda
memasangnya tanpa tahu, gejalanya akan terlihat persis seperti fitur
yang rusak: tombol Simpan & Close mati di layar yang kemarin masih
bisa dipakai.

Langkah pertama setelah memasang ada di bagian 8.


================================================
1. YANG DIMINTA, DAN KENAPA BENTUKNYA JADI BEGINI
================================================
Permintaannya: Close BD bisa dilanjutkan walaupun laporan Pengecekan
Awal belum ada, dengan izin per unit seperti Izin Bypass HM.

Bagian "bisa dilanjutkan" sebenarnya SUDAH ADA sejak v406 — tapi hanya
untuk satu akun, dan tanpa batas apa pun. Di tangkapan layar Anda
kalimatnya berbunyi:

    "Akun ini berwenang menutup BD tanpa itu."

Itulah persoalannya. Wewenangnya melekat pada ORANG, bukan pada
keadaan. Sekali akun itu dipakai, syarat Pengecekan Awal praktis
berhenti berlaku untuk SELURUH armada, selamanya, tanpa ada yang
tercatat di mana pun.

Padahal alasan melewatinya selalu melekat pada SATU unit tertentu:
BD lama yang terlanjur berjalan sebelum aplikasi dipakai, unit yang
kondisinya membuat LOTO tidak bisa dipasang, unit yang sedang di luar
area. Itu keadaan pada unit, bukan sifat orangnya.

Jadi yang ditambahkan bukan "kemampuan melewati" — itu sudah ada —
melainkan BATASNYA.


================================================
2. DUA SYARAT, BUKAN SATU
================================================
Persis pola Izin Bypass HM (v487):

  SIAPA      hak 'aksi:bypass-pengecekan' di
             Master Data > Hak Akses Menu
             (NIK berakses penuh otomatis punya)

  UNIT MANA  terdaftar dan masih berlaku di
             Master Data > Izin Bypass Pengecekan Awal

DUA-DUANYA HARUS TERPENUHI. Salah satu saja tidak cukup.

Kenapa hak saja tidak cukup: sudah dijelaskan di bagian 1.

Kenapa daftar unit saja tidak cukup: unit yang didaftarkan bisa
dikerjakan siapa pun yang membuka layarnya. Kalau haknya tidak ikut
diperiksa, mendaftarkan satu unit berarti mengizinkan SEMUA orang
menutup BD unit itu tanpa bukti keselamatan.


================================================
3. DUA KEADAAN PENOLAKAN, DUA KALIMAT BERBEDA
================================================
Akun yang SUDAH berhak tapi unitnya belum didaftarkan akan melihat
penolakan yang — kalau tidak dibedakan — sama persis dengan penolakan
untuk akun yang memang tidak berwenang.

Tidak ada cara menebak bahwa yang kurang justru pendaftaran unitnya.
Orangnya akan mencari kesalahan di tempat yang salah: menyalahkan
haknya, meminta hak baru, atau menyerah dan mengisi pengecekan
seadanya.

Jadi di bawah spanduk merah muncul baris kuning tersendiri:

    ℹ️ Akun Anda berhak melewati syarat Pengecekan Awal, tapi izinnya
       diberikan per unit — DT3010-63 belum terdaftar (atau izinnya
       sudah berakhir) di Master Data › Izin Bypass Pengecekan Awal.

Warnanya sengaja dibedakan dari penolakan di atasnya: ini bukan
larangan, melainkan petunjuk apa yang harus dikerjakan supaya bisa.

KALIMAT INI TIDAK MUNCUL untuk akun yang tidak berhak. Menawarkan cara
mendaftarkan unit kepada orang yang tetap tidak akan bisa memakainya
hanya memindahkan kebingungan, dan menghasilkan permintaan pendaftaran
unit yang sebenarnya tidak menyelesaikan apa-apa.


================================================
4. EMPAT LAYAR, BUKAN DUA
================================================
Saya kira aturan ini dipakai dua layar. Ternyata empat:

  MonitoringUnit.vue   Close BD          (menutup BD)
  MonitoringUnit.vue   Edit Pelaporan    (menyimpan part BARU)
  TiketPerbaikan.vue   Close Tiket       (menutup BD yang sama)
  TodoList.vue         Semua Outstanding (menyimpan part BARU)

TodoList nyaris terlewat. Kalau terlewat, layar itu jadi jalan pintas
yang membatalkan aturannya — dan orang akan memakai jalan pintas itu
justru karena lebih cepat.

SATU PERBEDAAN DI TODOLIST YANG PERLU DISEBUT

Di tiga layar lain, daftar izin dimuat saat halaman dibuka dan
disegarkan lagi saat modal dibuka — jadi saat tombol ditekan, daftarnya
sudah ada.

Popup di TodoList bisa jadi layar PERTAMA yang menyentuh aturan ini
dalam satu sesi. Jadi di sana daftarnya DITUNGGU (await) sebelum
keputusan diambil.

Kalau tidak ditunggu: daftar yang belum termuat berarti TOLAK, sehingga
akun yang sebenarnya berhak akan ditolak pada percobaan pertama lalu
berhasil pada percobaan kedua. Itu jenis kegagalan yang paling sulit
dipercaya orang lain, karena tidak bisa diulang.


================================================
5. DAFTAR GAGAL DIMUAT = TOLAK, BUKAN IZINKAN SEMUA
================================================
Kalau permintaan daftar izin gagal — backend mati, jaringan putus,
backend belum direstart sehingga endpoint-nya belum ada — jawabannya
TIDAK ADA yang diizinkan.

Untuk syarat keselamatan, ragu-ragu harus berarti tolak. Kebalikannya
(anggap semua boleh saat daftarnya tidak terbaca) berarti satu gangguan
jaringan sesaat membuka seluruh armada tanpa ada yang sadar.

Dan kegagalannya TIDAK MENGUNCI: penanda "sudah termuat" sengaja tidak
disetel saat gagal, jadi pembukaan modal berikutnya mencoba lagi —
bukan terkunci kosong sampai halaman dimuat ulang.

BENTUK JAWABAN DIPERIKSA, BUKAN DIANGGAP BENAR.
Pola `r.data?.data || []` menelan segala jawaban tak terduga menjadi
daftar kosong, dan di layar itu terbaca persis seperti "memang belum
ada unit yang diizinkan". Backend yang belum direstart menghasilkan
tepat keadaan itu — tanpa galat, tanpa 404. Jadi jawaban yang bentuknya
salah diperlakukan sebagai KEGAGALAN dan disebutkan di console.


================================================
6. SERVER MENCATAT, TIDAK MENOLAK — DAN INI DISENGAJA
================================================
Sampai v548 TIDAK ADA penjaga Pengecekan Awal di server sama sekali.
Penjaganya murni di layar. Saya tidak menambahkannya di update ini.

ALASANNYA

Menambahkan penjaga di server berarti menegakkan syarat Pengecekan Awal
pada SEMUA jalur yang menutup BD, bukan hanya yang lewat layar Close.
Jalur lain yang selama ini sah akan ikut terblokir:

  - kartu shift yang menulis Stop_Perbaikan
  - impor Excel
  - Close Tiket dari TiketPerbaikan
  - jalur mana pun yang belum saya petakan

Itu pembatasan BARU yang tidak Anda minta, dan akibatnya baru terasa
di lapangan setelah dipasang. Kalau penjaga server memang diinginkan,
itu perubahan tersendiri beserta pendataan jalur mana saja yang
terdampak — bilang saja.

YANG DITAMBAHKAN: JEJAKNYA

Sebelumnya pelewatan hanya dicatat dengan console.warn di peramban.
Console peramban hilang begitu tab ditutup — jadi pertanyaan "siapa
yang menutup BD ini tanpa pengecekan, kapan, dan unit apa" tidak pernah
bisa dijawab setelahnya.

Sekarang layar mengirim penanda `bypass_pengecekan: true` beserta
daftar apa yang kurang, dan server menuliskannya ke log:

  [bypass-pengecekan] NIK 31196 menutup BD #4821 (DT3010-63) tanpa
  Pengecekan Awal lengkap — kurang: Foto Loto, Foto Wheel Chock

Ini pelonggaran aturan keselamatan — justru jenis kejadian yang paling
perlu bisa ditelusuri.

CATATAN: `bypass_pengecekan` dan `bypass_kurang` BUKAN kolom tabel.
Handler PUT menyaring badan permintaan dengan `COLS.filter(c => c in
req.body)`, jadi kunci di luar daftar kolom diabaikan dengan sendirinya.


================================================
7. KENAPA TABELNYA TERPISAH DARI hm_bypass_unit
================================================
Kolomnya kembar persis. Menggabungkannya jadi satu tabel dengan kolom
"jenis" memang lebih ringkas.

Tapi itu berarti satu centang mengendalikan DUA pengaman yang tidak ada
hubungannya. Mencabut izin HM untuk satu unit akan ikut mencabut izin
pengecekannya — atau lebih buruk, memberi izin HM diam-diam memberi
izin melewati LOTO.

HM yang mundur adalah kesalahan DATA. Pengecekan Awal yang kosong
adalah pekerjaan keselamatan yang belum dilakukan, atau sudah dilakukan
tapi tidak terbukti. Dua hal itu tidak boleh dikendalikan satu saklar.

TAPI LOGIKANYA SATU

Endpoint keduanya sekarang dilayani fungsi yang SAMA (daftarIzin,
daftarIzinAktif, simpanIzin di masterdata.js). Sampai v548 hanya ada
satu daftar, jadi logikanya ditulis langsung di dalam endpoint-nya.
Begitu ada yang kedua, menyalinnya berarti dua salinan aturan "izin
sudah kedaluwarsa atau belum", "unit ini terdaftar atau tidak", dan
"menyimpan itu menyamakan, bukan menambah".

Salinan yang disalin akan berbeda cepat atau lambat, dan bedanya baru
ketahuan saat satu layar mengizinkan apa yang dilarang layar lain.

PERUBAHAN INI MENYENTUH JALUR BYPASS HM YANG SUDAH BEKERJA.
Perilakunya tidak diubah sama sekali — hanya letak kodenya. Tapi karena
ia jalur yang sudah berjalan, ada baiknya diuji sekali setelah memasang
(bagian 10, langkah 6).

ENDPOINT BARU: /aktif

Layar admin memakai /daftar — seluruh unit beserta keadaan izinnya.
Aplikasi sehari-hari memakai /aktif — hanya unit yang izinnya masih
berlaku, tanpa tipe, model, dan satker.

Bedanya penting: layar Close BD cuma perlu menjawab satu pertanyaan
ya/tidak, dan mengirim 551 baris lengkap untuk itu adalah muatan yang
tidak perlu ada di setiap pembukaan halaman.

Yang kedaluwarsa DIBUANG di /aktif, bukan dikirim dengan penanda. Layar
yang menerimanya tidak punya urusan lain dengan izin yang sudah
berakhir, dan mengirimkannya hanya menambah satu cara untuk salah.


================================================
8. LANGKAH PERTAMA SETELAH MEMASANG
================================================
A. BERI HAKNYA

   Master Data > Hak Akses Menu
   Cari baris: 🦺 Tutup BD tanpa Pengecekan Awal lengkap

   Bawaannya TERTUTUP untuk SEMUA jenis akun. Itu disengaja: yang
   dilewati bukan kesalahan pencatatan melainkan bukti keselamatan,
   dan sebuah update yang dipasang tidak boleh memberi siapa pun
   kemampuan itu. Harus ada yang memutuskannya.

   NIK 31196 tidak perlu saklar ini — sudah otomatis.

   >>> INI KEPUTUSAN ANDA: apakah Supervisor ikut diberi, atau cukup
       akun berakses penuh saja seperti perilaku sebelumnya.

B. DAFTARKAN UNITNYA

   Master Data > Izin Bypass Pengecekan Awal
   Centang unitnya, isi Alasan dan Berlaku sampai, tekan Simpan Daftar.

   Untuk kasus di tangkapan layar Anda: centang DT3010-63.

MENYIMPAN ITU MENYAMAKAN, BUKAN MENAMBAHKAN.
Unit yang centangnya dilepas akan DICABUT izinnya. Konsekuensinya
ditampilkan SEBELUM ditekan — ada baris "N akan DICABUT izinnya", dan
jumlah centang yang sedang tersembunyi oleh saringan ikut disebutkan.

ISI "BERLAKU SAMPAI" BILA MEMUNGKINKAN.
Dikosongkan = tanpa batas waktu. Untuk izin keselamatan itu sebaiknya
dihindari: izin yang dibuka untuk satu kejadian lalu lupa ditutup tidak
akan ada yang menemukan — tidak ada layar yang menampilkan "izin yang
masih terbuka sejak tiga bulan lalu". Kueri untuk mencarinya ada di
TABEL_IZIN_BYPASS_PENGECEKAN.sql.

PERUBAHAN LANGSUNG TERASA. Daftar izin disimpan di memori peramban dan
dibaca layar Close BD tanpa bertanya lagi ke server. Menyimpan di layar
admin membuang simpanan itu, dan setiap modal Close yang dibuka akan
mengambil daftar terbaru. Jadi izin yang baru DICABUT langsung berhenti
berlaku — tidak menunggu halaman dimuat ulang.


================================================
9. BERKAS SQL TIDAK WAJIB DIJALANKAN
================================================
Tabel pengecekan_bypass_unit dibuat sendiri saat backend menyala, dan
dipastikan lagi setiap kali endpoint izin dipanggil.

TABEL_IZIN_BYPASS_PENGECEKAN.sql hanya diperlukan kalau backend tidak
bisa direstart sekarang, atau Anda ingin mendaftarkan unit lewat SQL.
Isinya juga memuat kueri untuk melihat izin yang masih terbuka.


================================================
10. CARA PASANG
================================================
1. Extract ke folder yang MEMUAT folder Digitalisasi_3_3, lalu timpa.
   >>> App.vue ada di frontend/src/, BUKAN frontend/src/views/ <<<
   >>> api.js WAJIB ikut ditimpa — aturannya ada di situ <<<

2. RESTART BACKEND.

3. cd frontend && npm run build

4. Ctrl+Shift+R di peramban.

5. UJI JALUR BARUNYA:
   a. Buka Close BD pada unit yang BELUM didaftarkan, dengan akun yang
      SUDAH diberi hak. Harus muncul spanduk merah + baris kuning yang
      menyebut unitnya belum terdaftar.
   b. Daftarkan unit itu di Master Data > Izin Bypass Pengecekan Awal.
   c. Buka lagi Close BD-nya TANPA memuat ulang halaman. Spanduknya
      harus berubah jadi kuning "dilewati", dan Simpan & Close hidup.
   d. Tutup BD-nya. Periksa log backend, harus ada baris
      [bypass-pengecekan].

6. UJI JALUR LAMANYA (karena kodenya ikut dipindah):
   Master Data > Izin Bypass HM per Unit harus tetap memuat daftar unit
   dan tetap bisa menyimpan. Kalau layarnya kosong, itu tanda backend
   belum direstart.


================================================
YANG SUDAH DIPERIKSA
================================================
- Keputusan izin diuji terhadap 13 keadaan, semuanya benar:
      berhak + unit terdaftar                    LOLOS
      berhak + unit TIDAK terdaftar              ditolak, sebab disebut
      berhak + ejaan beda huruf besar/spasi      LOLOS (dinormalkan)
      TIDAK berhak + unit terdaftar              ditolak
      TIDAK berhak + tidak terdaftar             ditolak
      berhak + unit kosong / null                ditolak — pemanggil
                                                 yang lupa menyertakan
                                                 unit TIDAK mendapat
                                                 wewenang tanpa batas
      berhak + daftar izin kosong                ditolak
      daftar GAGAL dimuat                        ditolak, bukan
                                                 izinkan semua
      izin kedaluwarsa                           ditolak (disaring
                                                 server, tidak terkirim)
  Dan kalimat sebab: muncul untuk yang berhak-tapi-belum-terdaftar,
  KOSONG untuk yang tidak berhak, KOSONG untuk yang memang boleh.

- Penyimpanan daftar diuji terhadap database palsu, 7 keadaan:
      tambah pertama                             tersimpan
      tambah kedua                               yang lama tidak ganda
      lepas satu centang                         DICABUT (menyamakan)
      unit tidak dikenal                         ditolak & disebut
                                                 namanya, tidak ikut
                                                 tersimpan
      duplikat 'rt-032' / ' RT-032 ' / 'RT-032'  lebur jadi SATU baris,
                                                 bukan tiga yang bentrok
                                                 dengan UNIQUE KEY
      daftar kosong                              seluruhnya dicabut

- masterdata.js, hakedit.js, dan maintenancerecord.js lolos
  `node --check`
- 40 berkas .vue lolos @vue/compiler-sfc: parse, compileScript,
  compileTemplate — 0 bermasalah
- `vite build` DIJALANKAN dan BERHASIL (15,4 detik)
- Seluruh pemanggilan bolehLewatiPengecekan() ditelusuri ulang setelah
  tanda tangannya berubah. Fungsi yang dulu dipanggil tanpa argumen
  sekarang WAJIB menyertakan unit; pemanggilan yang terlewat akan
  menjawab TOLAK, bukan diam-diam mengizinkan. Empat pemanggil
  ditemukan dan keempatnya sudah disesuaikan.
- Urutan route diperiksa: /bypass-pengecekan/* berada DI ATAS route
  generik '/:type' dan '/:type/:id'. Kalau di bawah, ia akan tertangkap
  sebagai type='bypass-pengecekan', id='daftar' dan membalas daftar
  KOSONG tanpa galat — persis gejala v489.


================================================
YANG BELUM SAYA UJI
================================================
Tidak ada MySQL di tempat saya bekerja. CREATE TABLE dan seluruh query
terhadap pengecekan_bypass_unit belum pernah dijalankan terhadap
database sungguhan — yang dipastikan baru sintaks dan bentuk querynya.

Langkah 5 dan 6 di bagian 10 ada persis untuk membuktikannya dalam dua
menit.

Satu hal lagi yang tidak bisa saya uji sendiri: apakah ada jalur lain
di aplikasi ini yang menutup BD tanpa melewati keempat layar di bagian
4. Kalau ada, jalur itu tidak terkena aturan ini sama sekali — lihat
bagian 6.
